import re
import requests
import yaml
from string import Template
from common.get_env_config import GetURL
from common.get_log import log
from common.get_path import GETPath
from common.read_config import ReadOtherConfig


class Base:
    r = requests.session()
    url = GetURL()
    config = ReadOtherConfig()
    getpath = GETPath()

    def get_token(self, username, password):
        """
        此方法为获取电子班牌的token
        :param username:
        :param password:
        :return:
        """
        # 打开单点登录页面
        LoginSinglePage_Resp = self.r.get(self.url.geturl1('/login'))
        # 获取登录页面隐藏的session
        sessionpattern = '<input\s+?type="hidden"\s+?name="_csrf"\s+?value="(.*?)"\s+?/>'
        csrf = re.findall(sessionpattern, LoginSinglePage_Resp.text)
        # 提交登录
        self.r.post(self.url.geturl1('/login'),
                    {"username": username,
                     "password": password,
                     "_csrf": csrf})
        self.r.get(self.url.geturl2('/is-login'))
        self.r.get(self.url.geturl2('/login'))
        resp2 = self.r.get(self.url.geturl2('/is-login'))
        token = resp2.request.headers['Cookie'].split('; ')[1]
        if "OATH" in token:
            log.info(f"成功获取电子班牌系统{username}的token:{token}")
        else:
            log.error("未获取到电子班牌token")
        return token

    def send_api(self, req_body: dict):
        """
        封装requests代码，替代requests.request方法
        :param req: 传入请求的字典数据，包括method，url，params，json
        :return: 响应体
        传入的数据类型如下
        req_body={
            "method":"GET",
            "url":f"https://qyapi.weixin.qq.com/cgi-bin/gettoken?",
            "params":f"corpid={self.corpid}&corpsecret={secret}"
        }
        request的参数接收method，url，params，data，json
        把这些参数都写在一个字典里面，**req就可以解包
        变成method="get",url="http://xxx"，符合request传参的需求
        """
        res = self.r.request(**req_body)
        return res

    def load_yaml(self, path, sub=None):
        """
        封装yaml读取的代码，通过路径直接读取yml文件并转化成python数据类型
        :param path: yml文件的相对路径
        :param sub: 读取yml文件的二级数据目录，默认为None
        :return: 返回yml文件的python数据
        """
        # 链接根路径和yml文件的相对路径，简化文件路径
        realpath = self.getpath.getpath(path)
        with open(realpath, encoding="utf-8") as f:
            # 不写sub就获取yml的所有内容，写就获取yml的二级数据目录
            if sub is None:
                return yaml.safe_load(f)
            else:
                return yaml.safe_load(f)[sub]

    def template(self, path, data,sub=None):
        """
        使用模板技术，把yml文件中的变量进行二次转化，是本框架的yml文件的技术基础
        :param path: 模板技术输入yml文件完整路径
        :param data: data是需要修改的模板变量的字典类型
        :param sub: sub是对yml的数据进行二次提取，等于是一个大字典，再提取下一层的小字典，为了让一个yml文件可以有多个接口数据
        :return:
        """
        with open(path, encoding="utf-8") as f:
            if sub is None:
                '''
                不需要对数据进行二次提取，Template(f.read()).substitute(data)先替换变量
                yaml.safe_load把yml格式的字符串变成dict类型返回
                '''
                return yaml.safe_load(Template(f.read()).substitute(data))
            else:
                '''
                由于Template需要替换全部的变量，有漏的就会报错，先写Template(f.read()).substitute(data)
                就会报错，data只对sub下一层的数据改，并没有改其他层的数据，肯定会报错
                需要先yaml.safe_load(f)[sub]提取到下一层的数据，但由于是dict
                要通过yaml.dump转化成yml格式的字符串，经过Template来改变变量，最后在yaml.safe_load转化成dict
                '''
                return yaml.safe_load(Template(yaml.dump(yaml.safe_load(f)[sub])).substitute(data))

    def send_api_data(self, path, p_data, sub):
        """
        1.进一步优化封装请求，是本框架的第二个核心技术要点
        2.解决了如何非必填字段的问题，接口测试的非必填字段，只需要传入None值就好
        2.1 本方法就是解决如何传入None的问题
        :param path: 存放yml的api数据的相对路径
        :param p_data: Template模板里面，二次转化的数据
        :param sub: yml的二级数据目录，区别同一个api类中不同的api方法，比如add，delete
        :return: 返回请求体的字典类型
        """
        realpath = self.getpath.getpath(path) #获取该接口文件完整路径
        # 获取请求数据
        data: dict = self.template(realpath, p_data, sub)
        url = self.url.geturl2(data["path"])  # 获取真实url
        del data["path"]  # 删除请求数据中的path字段
        data["url"] = url  # 将真实url键值对放进字典
        resp = self.send_api(data)
        return resp


if __name__ == '__main__':
    pass